"""
The MIT License (MIT)
Copyright © 2020 Walkline Wang (https://walkline.wang)
https://gitee.com/walkline/esp32-ble
"""
from machine import Pin, PWM, Timer
from utime import sleep_ms


class  LedPadException(Exception):
	pass


class LedBase(object):
	def __init__(self, io, pwm=True):
		if pwm:
			self.__pin = PWM(Pin(io, Pin.OUT, Pin.PULL_DOWN, value=0), freq=1000000, duty=0)
		else:
			self.__pin = Pin(io, Pin.OUT, Pin.PULL_DOWN, value=0)
		
		self.__pwm = pwm
		self.__brightness = LedPad.Brightness.MEDIUM
	
	def deinit(self):
		if self.__pwm:
			self.__pin.deinit()
		else:
			self.__pin = None

	def set_brightness(self, value):
		self.__brightness = value
	
	def on(self):
		if self.__pwm:
			self.__pin.duty(self.__brightness)
	
	def off(self):
		if self.__pwm:
			self.__pin.duty(0)

	def duty(self, value):
		if self.__pwm:
			self.__pin.duty(value)


class LedPad(object):
	"""
	5*4 LED 矩阵驱动
	"""
	class Brightness(object):
		NONE = const(0)
		LOW = const(20)
		MEDIUM = const(60)
		HIGH = const(200)

	class Mode(object):
		OFF = const(0)
		ON = const(1)
		BREATH = const(2)


	def __init__(self, pin_set=None):
		assert pin_set is not None and isinstance(pin_set, tuple) and len(pin_set) == 2, LedPadException("pin_set must be a tuple, e.g. ((row output io), (column input io))")

		self.__row_io_set = []
		self.__column_io_set = []

		try:
			for io in pin_set[0]:
				self.__row_io_set.append(LedBase(io))
			
			for io in pin_set[1]:
				self.__column_io_set.append(LedBase(io, False))
		except IndexError:
			raise LedPadException("pin_set value error")

		self.__row_count = len(self.__row_io_set)
		self.__column_count = len(self.__column_io_set)

		assert self.__row_count > 0 and self.__column_count > 0, LedPadException("pin_set value error")

		self.__brightness = self.Brightness.MEDIUM
		self.__mode = self.Mode.BREATH
		self.__timer = Timer(12)
		self.__timer_start = False
		self.__duty = 0
		self.__duty_step = 10

	def __timer_cb(self, timer):
		for io in self.__row_io_set:
			io.duty(self.__duty)
		
		self.__duty += self.__duty_step

		if self.__duty == 0 or self.__duty >= self.__brightness:
			self.__duty_step = - self.__duty_step

	def deinit(self):
		self.__timer.deinit()

		for io in self.__row_io_set:
			io.deinit()
		for io in self.__column_io_set:
			io.deinit()
		
		self.__timer = None
		self.__row_io_set = None
		self.__column_io_set = None

	@property
	def brightness(self):
		return self.__brightness
	
	@brightness.setter
	def brightness(self, value):
		self.__brightness = value

		for io in self.__row_io_set:
			io.set_brightness(self.brightness)

	@property
	def mode(self):
		return self.__mode
	
	@mode.setter
	def mode(self, value):
		self.__mode = value

	def start(self):
		if self.__timer_start:
			self.__timer.deinit()
		
		if self.__mode == self.Mode.OFF:
			for io in self.__row_io_set:
				io.off()
		elif self.__mode == self.Mode.ON:
			for io in self.__row_io_set:
				io.on()
		elif self.__mode == self.Mode.BREATH:
			self.__duty = 0
			self.__timer.init(
				mode=Timer.PERIODIC,
				period=40,
				callback=self.__timer_cb
			)

			self.__timer_start = True

	def get_led_count(self):
		"""
		获取 Led 数量
		"""
		return self.__row_count * self.__column_count

	def self_check(self):
		"""
		灯光自检
		"""
		for index in range(self.__row_count):
			self.__row_io_set[index].on()
			sleep_ms(200)
			self.__row_io_set[index].off()


def main():
	ROW_SET = (5, 17, 16, 4, 15) # for output
	COLUMN_SET = (22, 21, 19, 18) # for intput
	PIN_SET = (ROW_SET, COLUMN_SET)

	ledpad = LedPad(PIN_SET)
	print("LedPad led count:", ledpad.get_led_count())

	ledpad.brightness = LedPad.Brightness.HIGH
	ledpad.self_check()
	ledpad.start()
	# ledpad.deinit()
	# ledpad = None


if __name__ == "__main__":
	try:
		main()
	except KeyboardInterrupt:
		print("\nPRESS CTRL+D TO RESET DEVICE")
